using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;

using TrueCode.Core;
using TrueCode.Core.Aspects;
using TrueCode.Core.Diagnostics;

namespace EMan.Interface.Common
{
	public abstract class Presenter : IDisposable
	{
	  public abstract void Dispose();
	}


	public abstract partial class Presenter< TIView > : Presenter, IPresenter<TIView>, IPresenterModel where TIView : class, IView
	{
		#region class State
		protected enum State
		{
			NotActive = 0,
			Ready = 1,
		}
		#endregion

		#region Fields
		private readonly IDiagnostic _diagnostic;

		private readonly object _stateAccess = new object();
		private State _state = State.Ready;

		private readonly object _pendingActivitiesAccess = new object();
		private readonly List< Activity > _pendingActivities = new List< Activity >();

		private TIView _view;
		#endregion

		#region .ctor
		protected Presenter([NotNull]IDiagnostic diagnostic)
		{
			#region Validation
			ExceptionHelper.ThrowIfArgumentIsNull<Presenter>(diagnostic, "diagnostic");
			#endregion

			_diagnostic = diagnostic;
		}
		#endregion

		#region Properties

		protected IDiagnostic Diagnostic { get { return _diagnostic; } }

		protected State CurrentState
		{
			get { return _state; }
			set
			{
				lock(_stateAccess)
				{
					_state = value;
				  if( _state == State.NotActive )
				    AbandonPendingActivities();
				}
			}
		}

		#endregion

		private void AbandonPendingActivities()
		{
			WaitHandle[] notAbandonedActivitiesWaitHandles;
			lock(_pendingActivitiesAccess)
			{
				notAbandonedActivitiesWaitHandles = _pendingActivities.Where(activity => !activity.Abandon())
					.Select(activity => activity.FinishedEvent)
					.ToArray();
			}
			if(notAbandonedActivitiesWaitHandles.Length > 0)
			{
				WaitHandle.WaitAll(notAbandonedActivitiesWaitHandles);
			}
			lock(_pendingActivitiesAccess)
			{
				_pendingActivities.Clear();
			}
		}

		#region View
		public TIView View
		{
			get
			{
			  if( _view == null ) throw new Exception< Presenter< TIView > >( "There is no view attached." );
			  return _view;
			}
      set
      {
        if(_view != null) throw  new InvalidOperationException("View can be set only once.");
        _view = value;
      }
		}

	  public virtual object DataContext
	  {
	    get { return  this; }
	  }
	  #endregion

		#region ExceptionHelper
		protected void TraceExceptionAndShow(Exception ex, string message, MessageType messageType)
		{
			Diagnostic.WriteException(this, ex);
			View.ShowMessage(message, messageType);
		}

		protected void TraceExceptionAndShowAsWarning(Exception ex, string message)
		{
			TraceExceptionAndShow(ex, message, MessageType.Warning);

		}

		protected void TraceExceptionAndShowAsError(Exception ex, string message)
		{
			TraceExceptionAndShow(ex, message, MessageType.Error);
		}
		#endregion

		public virtual bool QueryStop()
		{
			return true;
		}

		public virtual void Stop()
		{
			CurrentState = State.NotActive;
		}

	  #region Implementation of INotifyPropertyChanged
	  public event PropertyChangedEventHandler PropertyChanged;

	  protected void RaisePropertyChanged< T >( Expression< Func< T > > property )
	  {
	    PropertyChanged.Raise( this, property );
	  }

    protected void RaisePropertyChanged( string propertyName )
    {
      PropertyChanged.Raise( this, propertyName );
    }
	  #endregion
	}
}